package system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import common.config.datasource.DataSourceConstants;
import common.config.exception.CustomException;
import common.config.jwt.JwtUtil;
import common.config.jwt.TokenInfo;
import common.model.converter.CommonSysAuthorityConverter;
import common.model.dto.LoginToken;
import common.model.enums.RoleIsAdminEnum;
import common.model.enums.SysAuthorityTypeEnum;
import common.model.enums.UserDeletedEnum;
import common.model.po.*;
import common.model.request.base.PaginationRequest;
import common.model.response.base.Result;
import common.model.vo.ButtonVo;
import common.model.vo.MenuVo;
import common.service.CommonSysAuthorityService;
import common.service.CommonSysRoleAuthorityService;
import common.service.CommonSysUserRoleService;
import common.service.RedissonCacheService;
import common.util.CompareUtil;
import common.util.HttpRequestUtil;
import common.util.RouteUtil;
import org.redisson.api.RMap;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import system.mapper.SysRoleMapper;
import system.model.converter.SysRoleConverter;
import system.model.request.SaveRoleRequest;
import system.model.request.SaveUserRoleRequest;
import system.model.request.SearchRoleRequest;
import system.model.response.SaveUserRoleResponse;
import system.service.SysRoleService;
import system.service.SysUserRoleService;
import system.service.SysUserService;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 服务实现类
 *
 * @author 米泽鹏
 * @since 2022-03-22
 */
@Service
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRole> implements SysRoleService {

	@Resource
	private SysRoleMapper mapper;
	@Resource
	private CommonSysUserRoleService commonUserRoleService;
	@Resource
	private CommonSysAuthorityService commonAuthorityService;
	@Resource
	private CommonSysRoleAuthorityService commonRoleAuthorityService;
	@Resource
	private SysUserRoleService userRoleService;
	@Resource
	private SysUserService userService;
	@Resource
	private SysRoleService roleService;
	@Resource
	private RedissonCacheService redissonCacheService;

	@Override
	public List<SysRole> getRolesByUserId(Long userId) {
		RMap<Long, Set<Long>> roleIdCacheByUserId = redissonCacheService.getAllRoleIdCacheByUserId();
		Set<Long> roleIdSet = roleIdCacheByUserId.get(userId);
		if (CollectionUtil.isNotEmpty(roleIdSet)) {
			List<SysRole> sysRoleCache = redissonCacheService.getSysRoleCache();
			return sysRoleCache.stream().filter(role -> roleIdSet.contains(role.getId())).collect(Collectors.toList());
		} else {
			return Collections.emptyList();
		}
	}

	@Override
	public Page<SysRole> pageRole(PaginationRequest<SearchRoleRequest, SysRole> paginationRequest) {
		LambdaQueryWrapper<SysRole> queryWrapper = paginationRequest.buildQueryWrapper().lambda();
		queryWrapper.orderByAsc(SysRole::getId);
		return page(paginationRequest.buildPage(), queryWrapper);
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Result<?> saveRole(SaveRoleRequest request) {
		// 校验角色编码是否已被使用
		SysRole oneByRoleCode = lambdaQuery().eq(SysRole::getRoleCode, request.getRoleCode()).one();
		if (oneByRoleCode != null && !oneByRoleCode.getId().equals(request.getId())) {
			throw new CustomException("角色编码已被使用");
		}
		SysRole role = SysRoleConverter.saveRequestToPo(request);
		if (role.getId() == null) {
			save(role);
		} else {
			updateById(role);
		}
		// 更新缓存
		redissonCacheService.cacheSysRole(list()).throwExceptionIfFailed();
		return Result.success();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	public Result<?> deleteRole(Long id) {
		List<SysUserRole> userRoleList = commonUserRoleService.lambdaQuery().eq(SysUserRole::getRoleId, id).list();
		if (!userRoleList.isEmpty()) {
			throw new CustomException("有用户正在使用该角色，无法删除");
		}
		// 删除角色
		removeById(id);
		// 更新缓存
		redissonCacheService.cacheSysRole(list()).throwExceptionIfFailed();
		return Result.success();
	}

	@Override
	@Transactional(rollbackFor = Exception.class)
	@DS(DataSourceConstants.MASTER_DATASOURCE)
	public Result<?> saveUserRole(SaveUserRoleRequest request) {
		SaveUserRoleResponse saveUserRoleResponse = null;
		// 修改前，当前登录用户的角色
		LoginToken loginToken = JwtUtil.parseLoginToken(JwtUtil.getTokenStringFromRequest(HttpRequestUtil.getRequest()));
		List<SysRole> roleList = getRolesByUserId(loginToken.getUserId());
		Set<Long> roleIdSet = roleList.stream().map(SysRole::getId).collect(Collectors.toSet());
		// 现有关联关系
		List<Long> currentUserIdList = commonUserRoleService.lambdaQuery().eq(SysUserRole::getRoleId, request.getRoleId()).list().stream().map(SysUserRole::getUserId).collect(Collectors.toList());
		// 新的关联关系
		List<Long> destinationUserIdList = request.getUserIdList();
		// 需要删除的关联关系
		List<Long> deleteUserIdList = CompareUtil.getDeleteIdList(currentUserIdList, destinationUserIdList);
		// 需要新增的关联关系
		List<Long> insertUserIdList = CompareUtil.getInsertIdList(currentUserIdList, destinationUserIdList);
		if (!deleteUserIdList.isEmpty()) {
			commonUserRoleService.lambdaUpdate().eq(SysUserRole::getRoleId, request.getRoleId()).in(SysUserRole::getUserId, deleteUserIdList).remove();
		}
		List<SysUserRole> insertUserRoleList = Lists.newArrayList();
		for (Long userId : insertUserIdList) {
			SysUserRole userRole = new SysUserRole();
			userRole.setUserId(userId);
			userRole.setRoleId(request.getRoleId());
			insertUserRoleList.add(userRole);
		}
		commonUserRoleService.saveBatch(insertUserRoleList);
		// 更新缓存
		if (!deleteUserIdList.isEmpty() || !insertUserIdList.isEmpty()) {
			redissonCacheService.cacheSysUserRole(commonUserRoleService.getAllSysUserRole()).throwExceptionIfFailed();
		}
		// 如果改变了当前登录用户的角色，则生成并返回新的token
		if (roleIdSet.contains(request.getRoleId()) && deleteUserIdList.contains(loginToken.getUserId()) || !roleIdSet.contains(request.getRoleId()) && insertUserIdList.contains(loginToken.getUserId())) {
			HashSet<Long> newRoleIdSet = new HashSet<>(roleIdSet);
			// 原有角色被移除
			if (roleIdSet.contains(request.getRoleId())) {
				newRoleIdSet.remove(request.getRoleId());
			}
			// 新角色被添加
			else {
				newRoleIdSet.add(request.getRoleId());
			}
			// 查询该用户具有的权限
			List<SysUserRole> newUserRoleList = userRoleService.lambdaQuery().eq(SysUserRole::getUserId, loginToken.getUserId()).list();
			List<SysRoleAuthority> roleAuthorityList = commonRoleAuthorityService.lambdaQuery().in(SysRoleAuthority::getRoleId, newUserRoleList.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet())).list();
			List<Long> authorityIdList = roleAuthorityList.stream().map(SysRoleAuthority::getAuthId).distinct().collect(Collectors.toList());
			LoginToken newLoginToken = new LoginToken(loginToken.getUserId(), loginToken.getUsername(), loginToken.getNickname(), newRoleIdSet, new HashSet<>(authorityIdList));
			TokenInfo tokenInfo = JwtUtil.createLoginToken(newLoginToken);
			saveUserRoleResponse = new SaveUserRoleResponse();
			saveUserRoleResponse.setToken(tokenInfo.getToken());
			saveUserRoleResponse.setExpire(tokenInfo.getExpireDate());
			// 更新token缓存
			redissonCacheService.cacheLoginTokenString(loginToken.getUserId(), tokenInfo.getToken(), tokenInfo.getExpireDate());
			// 返回新的菜单和按钮权限
			if (CollectionUtil.isNotEmpty(authorityIdList)) {
				Set<ButtonVo> buttons = Sets.newHashSet();
				List<SysAuthority> authorityList = commonAuthorityService.listByIds(authorityIdList);
				List<SysAuthority> authorityMenuList = Lists.newArrayList();
				authorityList.forEach(authority -> {
					if (SysAuthorityTypeEnum.MENU.getValue().equals(authority.getType()) || SysAuthorityTypeEnum.STRUCTURE.getValue().equals(authority.getType())) {
						authorityMenuList.add(authority);
					}
					if (SysAuthorityTypeEnum.BUTTON.getValue().equals(authority.getType())) {
						ButtonVo buttonVO = CommonSysAuthorityConverter.poToButtonVO(authority);
						buttons.add(buttonVO);
					}
				});
				Set<MenuVo> menus = RouteUtil.buildRoutes(authorityMenuList);
				saveUserRoleResponse.setMenus(menus);
				saveUserRoleResponse.setButtons(buttons);
			} else {
				saveUserRoleResponse.setMenus(Collections.emptySet());
				saveUserRoleResponse.setButtons(Collections.emptySet());
			}
			// 是否是管理员
			List<SysRole> newRoleList = roleService.listByIds(newUserRoleList.stream().map(SysUserRole::getRoleId).collect(Collectors.toSet()));
			saveUserRoleResponse.setIsAdmin(newRoleList.stream().anyMatch(role -> RoleIsAdminEnum.IS_ADMIN.getValue().equals(role.getIsAdmin())));
		}
		return Result.successWithData(saveUserRoleResponse);
	}

	@Override
	public Result<Map<String, List<?>>> getUserRoleInfo(Long roleId) {
		Map<String, List<?>> map = new HashMap<>(2);
		// 所有用户
		List<SysUser> userList = userService.lambdaQuery().eq(SysUser::getIsDeleted, UserDeletedEnum.NOT_DELETED.getValue()).list();
		// 该角色下的用户
		List<Long> identifiedUserIdList = userRoleService.getNotDeletedUserIdByRoleId(roleId);
		map.put("userList", userList);
		map.put("identifiedUserIdList", identifiedUserIdList);
		return Result.successWithData(map);
	}

}
